/*
 * Copyright (c) 2020 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "camera_manager.h"

using namespace std;
using namespace OHOS;
using namespace OHOS::Media;

static int32_t SampleGetRecordFd()
{
    struct timeval tv = {};
    gettimeofday(&tv, nullptr);
    struct tm *ltm = localtime(&tv.tv_sec);
    int32_t fd = -1;
    if (ltm != nullptr) {
        ostringstream ss("Capture_");
        ss << "Record" << ltm->tm_hour << "-" << ltm->tm_min << "-" << ltm->tm_sec << ".mp4";
        fd = open(("/sdcard/" + ss.str()).c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
        cout << "Open "
             << "/sdcard/" << ss.str() << endl;

        if (fd == -1) {
            cout << "Open recorder file failed. strerr=" << strerror(errno) << endl;
        }
    }
    return fd;
}

static void SampleSaveCapture(const char *p, uint32_t size)
{
    cout << "Start saving picture" << endl;
    struct timeval tv = {};
    gettimeofday(&tv, nullptr);
    struct tm *ltm = localtime(&tv.tv_sec);
    if (ltm != nullptr) {
        ostringstream ss("Capture_");
        ss << "Capture" << ltm->tm_hour << "-" << ltm->tm_min << "-" << ltm->tm_sec << ".jpg";

        ofstream pic("/sdcard/" + ss.str(), ofstream::out | ofstream::trunc);
        cout << "write " << size << " bytes" << endl;
        pic.write(p, size);
        pic.close();
        cout << "Saving picture end" << endl;
    }
}

Recorder *SampleCreateRecorder()
{
    int ret = 0;
    int32_t sampleRate = 48000;
    int32_t channelCount = 1;
    AudioCodecFormat audioFormat = AAC_LC;
    AudioSourceType inputSource = AUDIO_MIC;
    int32_t audioEncodingBitRate = sampleRate;
    VideoSourceType source = VIDEO_SOURCE_SURFACE_ES;
    int32_t frameRate = 30;
    double fps = 30;
    int32_t rate = 4096;
    int32_t sourceId = 0;
    int32_t audioSourceId = 0;
    int32_t width = 1920;
    int32_t height = 1080;
    VideoCodecFormat encoder;
    encoder = HEVC;
    width = 1920;
    height = 1080;
    Recorder *recorder = new Recorder();
    if ((ret = recorder->SetVideoSource(source, sourceId)) != SUCCESS) {
        cout << "SetVideoSource failed." << ret << endl;
        goto ERROR;
    }
    if ((ret = recorder->SetVideoEncoder(sourceId, encoder)) != SUCCESS) {
        cout << "SetVideoEncoder failed." << ret << endl;
        goto ERROR;
    }
    if ((ret = recorder->SetVideoSize(sourceId, width, height)) != SUCCESS) {
        cout << "SetVideoSize failed." << ret << endl;
        goto ERROR;
    }
    if ((ret = recorder->SetVideoFrameRate(sourceId, frameRate)) != SUCCESS) {
        cout << "SetVideoFrameRate failed." << ret << endl;
        goto ERROR;
    }
    if ((ret = recorder->SetVideoEncodingBitRate(sourceId, rate)) != SUCCESS) {
        cout << "SetVideoEncodingBitRate failed." << ret << endl;
        goto ERROR;
    }
    if ((ret = recorder->SetCaptureRate(sourceId, fps)) != SUCCESS) {
        cout << "SetCaptureRate failed." << ret << endl;
        goto ERROR;
    }
    if ((ret = recorder->SetAudioSource(inputSource, audioSourceId)) != SUCCESS) {
        cout << "SetAudioSource failed." << ret << endl;
        goto ERROR;
    }
    if ((ret = recorder->SetAudioEncoder(audioSourceId, audioFormat)) != SUCCESS) {
        cout << "SetAudioEncoder failed." << ret << endl;
        goto ERROR;
    }
    if ((ret = recorder->SetAudioSampleRate(audioSourceId, sampleRate)) != SUCCESS) {
        cout << "SetAudioSampleRate failed." << ret << endl;
        goto ERROR;
    }
    if ((ret = recorder->SetAudioChannels(audioSourceId, channelCount)) != SUCCESS) {
        cout << "SetAudioChannels failed." << ret << endl;
        goto ERROR;
    }
    if ((ret = recorder->SetAudioEncodingBitRate(audioSourceId, audioEncodingBitRate)) != SUCCESS) {
        cout << "SetAudioEncodingBitRate failed." << ret << endl;
        goto ERROR;
    }
    if ((ret = recorder->SetMaxDuration(36000)) != SUCCESS) { // 36000s=10h
        cout << "SetAudioEncodingBitRate failed." << ret << endl;
        goto ERROR;
    }
    return recorder;

ERROR:
    delete recorder;
    return nullptr;
}

void SampleFrameStateCallback::OnFrameFinished(
    Camera &camera, FrameConfig &fc, FrameResult &result)
{
    static int frameCount = 0;

    if (fc.GetFrameConfigType() == FRAME_CONFIG_CAPTURE) {

        list<Surface *> surfaceList = fc.GetSurfaces();
        for (Surface *surface : surfaceList) {
            SurfaceBuffer *buffer = surface->AcquireBuffer();
            if (buffer != nullptr) {
                uint8_t *virtAddr = static_cast<uint8_t *>(buffer->GetVirAddr());

                printf("---->> frame %d // size %d // %02X %02X %02X %02X %02X %02X \n",
                    frameCount++, buffer->GetSize(),
                    virtAddr[0], virtAddr[1], virtAddr[2], virtAddr[3], virtAddr[4], virtAddr[5]);

                // if (virtAddr != nullptr)
                //     SampleSaveCapture(virtAddr, buffer->GetSize());
            
                surface->ReleaseBuffer(buffer);
            }
            
            delete surface;
        }
    }
    else
        cout << "===== Receive frame complete inform. ===== " << fc.GetFrameConfigType() << endl;

    delete &fc;
}

void TestIBufferConsumerListener::OnBufferAvailable()
{
    printf("----- OnBufferAvailable ----- \n");

    list<Surface *> surfaceList = fc->GetSurfaces();
    for (Surface *surface : surfaceList)
    {
        SurfaceBuffer *buffer = surface->AcquireBuffer();
        if (buffer != nullptr)
        {
            uint8_t *virtAddr = static_cast<uint8_t *>(buffer->GetVirAddr());

            printf("---->> frame %d // size %d // %02X %02X %02X %02X %02X %02X \n",
                frameCount++, buffer->GetSize(),
                virtAddr[0], virtAddr[1], virtAddr[2], virtAddr[3], virtAddr[4], virtAddr[5]);

            // if (virtAddr != nullptr)
            //     SampleSaveCapture(virtAddr, buffer->GetSize());

            surface->ReleaseBuffer(buffer);
        }

        // delete surface;
    }
}

int SampleCameraStateMng::PrepareRecorder()
{
    if (cam_ == nullptr) {
        cout << "Camera is not ready." << endl;
        return -1;
    }
    if (recorder_ == nullptr) {
        recorder_ = SampleCreateRecorder();
    }
    if (recorder_ == nullptr) {
        cout << "Recorder not available." << endl;
        return -1;
    }
    if (recordFd_ == -1) {
        recordFd_ = SampleGetRecordFd();
    }
    if (recordFd_ == -1) {
        cout << "Create fd failed." << endl;
        return -1;
    }
    return SUCCESS;
}

void SampleCameraStateMng::StartRecord()
{
    int ret;
    if (recordState_ == STATE_RUNNING) {
        cout << "Camera is already recording." << endl;
        return;
    }
    // ret = PrepareRecorder();
    // if (ret != SUCCESS) {
    //     cout << "PrepareRecorder failed." << endl;
    //     CloseRecorder();
    //     return;
    // }
    // ret = recorder_->SetOutputFile(recordFd_);
    // if (ret != SUCCESS) {
    //     cout << "SetOutputPath failed. ret=" << ret << endl;
    //     CloseRecorder();
    //     return;
    // }
    // ret = recorder_->Prepare();
    // if (ret != SUCCESS) {
    //     cout << "Prepare failed. ret=" << ret << endl;
    //     CloseRecorder();
    //     return;
    // }
    // ret = recorder_->Start();
    // if (ret != SUCCESS) {
    //     cout << "recorder start failed. ret=" << ret << endl;
    //     CloseRecorder();
    //     return;
    // }
    // Surface *surface = (recorder_->GetSurface(0)).get();

    Surface *surface = Surface::CreateSurface();

    FrameConfig *fc = new FrameConfig(FRAME_CONFIG_RECORD);
    listener->fc = fc;
    surface->RegisterConsumerListener(*listener);

    surface->SetWidthAndHeight(1920, 1080);
    surface->SetQueueSize(3);
    surface->SetSize(1024 * 1024);

    fc->AddSurface(*surface);
    ret = cam_->TriggerLoopingCapture(*fc);
    if (ret != 0) {
        delete fc;
        CloseRecorder();
        cout << "camera start recording failed. ret=" << ret << endl;
        return;
    }
    recordState_ = STATE_RUNNING;
    cout << "camera start recording succeed." << endl;
}

void SampleCameraStateMng::StartPreview()
{
    if (cam_ == nullptr) {
        cout << "Camera is not ready." << endl;
        return;
    }
    if (previewState_ == STATE_RUNNING) {
        cout << "Camera is already previewing." << endl;
        return;
    }

    Surface *surface = Surface::CreateSurface();
    if (surface == nullptr) {
        cout << "CreateSurface failed" << endl;
        return;
    }

    FrameConfig *fc = new FrameConfig(FRAME_CONFIG_PREVIEW);

    surface->SetWidthAndHeight(1920, 1080); /* 1920:width,1080:height */
    surface->SetUserData("region_position_x", "0");
    surface->SetUserData("region_position_y", "0");
    surface->SetUserData("region_width", "800");
    surface->SetUserData("region_height", "480");

    fc->AddSurface(*surface);
    int32_t ret = cam_->TriggerLoopingCapture(*fc);
    if (ret != 0) {
        delete fc;
        cout << "camera start preview failed. ret=" << ret << endl;
        return;
    }
    
    delete surface;
    previewState_ = STATE_RUNNING;
    cout << "camera start preview succeed." << endl;
}

void SampleCameraStateMng::Capture()
{
    if (cam_ == nullptr) {
        cout << "Camera is not ready." << endl;
        return;
    }
    FrameConfig *fc = new FrameConfig(FRAME_CONFIG_CAPTURE);
    Surface *surface = Surface::CreateSurface();
    if (surface == nullptr) {
        delete fc;
        cout << "CreateSurface failed" << endl;
        return;
    }
    surface->SetWidthAndHeight(1920, 1080); /* 1920:width,1080:height */
    fc->AddSurface(*surface);
    cam_->TriggerSingleCapture(*fc);
}

void SampleCameraStateMng::Stop()
{
    if (cam_ == nullptr) {
        cout << "Camera is not ready." << endl;
        return;
    }
    cam_->StopLoopingCapture();
    if (recordState_ == STATE_RUNNING) {
        CloseRecorder();
    }
    recordState_ = STATE_IDLE;
    previewState_ = STATE_IDLE;
}
